package com.szlanyou.tcpservermaster;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

import com.szlanyou.tcpservermaster.util.DoneLogger;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * @author 陈章
 * desc:
 * Tcp服务端核心类，支持一对多。
 */

public class TcpSocketServer {

    private static final String TAG = "TcpSocketServer";

    private AtomicBoolean isRun = new AtomicBoolean(false);

    private ServerSocket serverSocket;

    private Map<String, TcpClientItem> tcpClientReceiverMap;

    private int port;
    /**
     * 用于Server内部的监听，用于map统一处理。
     */
    private ClientStatusImpl clientStatusListenerIn;

    /**
     * 外部client监听
     */
    private TcpClientItem.OnClientStatus clientStatusListenerOut;
    private OnServerStatusListener onServerStatusListener;
    private final Handler childHandler;

    private final int MSG_TYPE_ENABLE_SERVER = 10;
    private final int MSG_TYPE_CLOSE_SERVER = 20;
    private final int MSG_TYPE_RELEASE = 30;

    /**
     * 该callback运行于子线程
     */
    private class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_TYPE_ENABLE_SERVER:
                    enableServerPrivate();
                    break;
                case MSG_TYPE_CLOSE_SERVER:
                    closeServerPrivate();
                    callbackServerClosed();
                    break;
                case MSG_TYPE_RELEASE:
                    closeServerPrivate();
                    childHandler.removeCallbacksAndMessages(null);
                    childHandler.getLooper().quit();
                    break;
            }
            return false;
        }
    }

    private void callbackServerClosed() {
        if(onServerStatusListener != null){
            onServerStatusListener.onServerClosed();
        }
    }

    public TcpSocketServer(int port) {
        this.port = port;
        this.tcpClientReceiverMap = new ConcurrentHashMap<>();

        HandlerThread handlerThread = new HandlerThread("tcp_server " + this);
        handlerThread.start();
        //子线程Handler
        childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
    }

    public void enableServer() {
        if (!isRun.get()) {
            childHandler.obtainMessage(MSG_TYPE_ENABLE_SERVER).sendToTarget();
        }
    }

    public void closeServer() {
        if(isRun.get()){
            childHandler.obtainMessage(MSG_TYPE_CLOSE_SERVER).sendToTarget();
        }else{
            DoneLogger.e(TAG,"the server not started,why do you close it ?");
        }
    }

    private void enableServerPrivate() {
        initServer();
        clientStatusListenerIn = new ClientStatusImpl();
        while (isRun.get()) {
            handleClient();
        }
    }

    private void closeServerPrivate() {
        isRun.set(false);
        for (TcpClientItem receiver : tcpClientReceiverMap.values()) {
            receiver.closeClient();
        }
        if (serverSocket != null) {
            try {
                serverSocket.close();
                serverSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 处理接入的客户端
     */
    private void handleClient() {
        try {
            Socket socket = serverSocket.accept();
            //keep alive作用：避免对方宕机，还保持着连接，占用资源。https://blog.csdn.net/nanhuaibeian/article/details/104760165/
            socket.setKeepAlive(true);

            TcpClientItem receiver = new TcpClientItem(socket, clientStatusListenerIn);
            receiver.start();
            DoneLogger.d(TAG, "进来一个客户端:" + socket.getInetAddress().getHostAddress());
        } catch (IOException e) {
            e.printStackTrace();
            //服务端主动关闭会报错：java.net.SocketException: Socket closed
        }
    }

    private void initServer() {
        try {
            serverSocket = new ServerSocket(port);
            isRun.set(true);
            DoneLogger.d(TAG, "tcp 服务端创建成功");
            if(onServerStatusListener != null){
                onServerStatusListener.onServerStarted();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void listenClient(TcpClientItem.OnClientStatus clientStatusListenerOut){
        this.clientStatusListenerOut = clientStatusListenerOut;
    }

    public void listenServer(OnServerStatusListener onServerStatusListener){
        this.onServerStatusListener = onServerStatusListener;
    }

    public void release(){
        childHandler.obtainMessage(MSG_TYPE_RELEASE).sendToTarget();
    }

    public interface OnServerStatusListener{
        /**
         * 服务已经开启
         */
        void onServerStarted();

        /**
         * 服务已经关闭
         */
        void onServerClosed();
    }

    /**
     * 客户端监听
     */
    class ClientStatusImpl implements TcpClientItem.OnClientStatus {
        @Override
        public void onJoined(TcpClientItem client) {
            String address = client.getAddress();
            //保证之前连接同一个连接资源释放
            if(tcpClientReceiverMap.containsKey(address)){
                tcpClientReceiverMap.get(address).release();
            }
            tcpClientReceiverMap.put(address, client);
            if(clientStatusListenerOut != null){
                clientStatusListenerOut.onJoined(client);
            }
        }

        @Override
        public void onDisconnected(TcpClientItem client) {
            DoneLogger.d(TAG, client.getAddress() + " is disconnect");
            client.closeClient();
            tcpClientReceiverMap.remove(client.getAddress());
            if(clientStatusListenerOut != null){
                clientStatusListenerOut.onDisconnected(client);
            }
        }

        @Override
        public void onDisconnect(TcpClientItem client, Throwable e) {
            DoneLogger.d(TAG, client.getAddress() + " is disconnect");
            client.closeClient();
            tcpClientReceiverMap.remove(client.getAddress());
            if(clientStatusListenerOut != null){
                clientStatusListenerOut.onDisconnect(client,e);
            }
        }

        @Override
        public void onError(TcpClientItem client, Throwable e) {
            DoneLogger.d(TAG, client.getAddress() + " is Error");
            client.closeClient();
            tcpClientReceiverMap.remove(client.getAddress());
            if(clientStatusListenerOut != null){
                clientStatusListenerOut.onError(client,e);
            }
        }

        @Override
        public void onReceive(TcpClientItem client, InputStream in) {
            if(clientStatusListenerOut != null){
                clientStatusListenerOut.onReceive(client,in);
            }
        }

        @Override
        public void onSendMsgFailure(TcpClientItem client, Throwable e) {
            if(clientStatusListenerOut != null){
                clientStatusListenerOut.onSendMsgFailure(client,e);
            }
        }
    }

    public boolean isRun() {
        return isRun.get();
    }

}
